Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Update apiextensions/v1 for JSON class #3

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

kingdonb
Copy link

I'm not certain if this was a generated file, or if this was just missed when it was defined

Working against the main branch, I got this error:

/Users/kingdonb/.rvm/gems/ruby-2.7.5/bundler/gems/kube-dsl-507696e0e2b9/lib/kube-dsl/dsl/apiextensions/v1/json_schema_props.rb:55:in `<class:JSONSchemaProps>': uninitialized constant KubeDSL::DSL::Apiextensions::V1::JSON (NameError)

(Today, I am defining a CustomResourceDefinition in kube-dsl!)

I'm not certain if this was a generated file, or if this was just missed when it was defined

Working against the main branch, I got this error:
```
/Users/kingdonb/.rvm/gems/ruby-2.7.5/bundler/gems/kube-dsl-507696e0e2b9/lib/kube-dsl/dsl/apiextensions/v1/json_schema_props.rb:55:in `<class:JSONSchemaProps>': uninitialized constant KubeDSL::DSL::Apiextensions::V1::JSON (NameError)
```

(Today, I am defining a CustomResourceDefinition in kube-dsl!)
@kingdonb
Copy link
Author

Perhaps it simply wasn't finished yet...

I got stuck after that, here:

> puts crd.to_resource.to_yaml
NameError: undefined local variable or method `all_ofs' for #<KubeDSL::DSL::Apiextensions::V1::JSONSchemaProps:0x0000000154e94850>
Did you mean?  all_of
               all_oves
from /Users/kingdonb/.rvm/gems/ruby-2.7.5/bundler/gems/kube-dsl-507696e0e2b9/lib/kube-dsl/dsl/apiextensions/v1/json_schema_props.rb:93:in `block in serialize'

The changes that follow are just me noodling around until I could get the CRD to print itself out this way, (converting to draft)

Kingdon Barrett added 2 commits March 22, 2022 23:19
Signed-off-by: Kingdon Barrett <[email protected]>
Signed-off-by: Kingdon Barrett <[email protected]>
@kingdonb kingdonb changed the title Update apiextensions/v1 for JSON class WIP: Update apiextensions/v1 for JSON class Mar 23, 2022
@kingdonb
Copy link
Author

(there is no "draft PR" mode here I guess)

@kingdonb
Copy link
Author

kingdonb commented Mar 23, 2022

I was ultimately able to make a CRD that looks like this (it's not viable, but close):

> puts crd.to_resource.to_yaml
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: dailies.fluxwebstats.fluxcd.io
spec:
  group: fluxwebstats.fluxcd.io
  names:
    kind: Daily
    plural: dailies
    shortNames:
    - day
    singular: daily
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        properties:
          spec: |-
            {"type": "object",
            "properties": {
              "cronSpec"}
        type: object
    served: 'true'
    storage: true

I started from the example CRD here: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#create-a-customresourcedefinition

Where I got stuck was finding that OpenAPIv3Schema resource's properties field is a KeyValueFields object, but so is its properties.spec nested beneath that.

(I didn't find any examples of how to nest KeyValueFields! Probably because there aren't any, IDK)

Here's what I had so far: https://gist.github.com/kingdonb/f9753f79d291f71caff3164082523da2

I've got an actual goal in mind for what to do with this CRD, but I think I'll just create the rest manually now as I'm not sure how much value there is in building CRDs by hand in Ruby (the controller-runtime project usually generates them for you if you're doing this in Go, right? Based on the resources that are in the associated controller api modules...)

I know you showed me an example of a kubernetes operator in Ruby, I think it was this one:
https://gitlab.com/tobiaskuntzsch/kubernetes-operator

(FWIW, that example includes hard-coded CRD yamls too!)

It also uses kubeclient and I think I would prefer to use k8s-ruby but if you have an opinion about this I could probably be persuaded as I am not very deep into this project yet!

(I opened an issue on my own repo where I'll implement this, and linked it back here below, in case you are curious what kind of CRD operator I want to build in Ruby! It's nothing special, "baby's first CRD controller" seriously, but in Ruby...)

@camertron
Copy link
Member

camertron commented Mar 24, 2022

Hey @kingdonb, sorry about that! You're right, master is in a semi-broken state at the moment, what with me mucking around trying to get the next version of Kuby released. Holy cow there are so many moving parts... That being said, I just pushed up what I think is a fix to the problem you experienced. Turns out I forgot to write the corresponding autoload statement for KubeDSL::DSL::Apiextensions::V1::JSON.

First of all, it's so cool you're creating a CRD and an accompanying Ruby operator - I'm really curious to see how that process goes. The k8s world tends to be so dominated by Go, it's nice to see forays by other languages. Nothing against Go of course, but Ruby is cool too :)

Where I got stuck was finding that OpenAPIv3Schema resource's properties field is a KeyValueFields object, but so is its properties.spec nested beneath that.

(I didn't find any examples of how to nest KeyValueFields! Probably because there aren't any, IDK)

Ah right, there's no way to nest KeyValueFields. It looks like it's marked that way in the k8s schema, so short of changing k8s itself there's not much the generator can do. It would be really cool if there was an easy way to compose an open API schema using KubeDSL, potentially leveraging method_missing or some such. The k8s schema itself references a pretty large set of shared objects (like PodSpec, etc), and it would be nice to be able to specify them as types.

I've got an actual goal in mind for what to do with this CRD, but I think I'll just create the rest manually now as I'm not sure how much value there is in building CRDs by hand in Ruby (the controller-runtime project usually generates them for you if you're doing this in Go, right? Based on the resources that are in the associated controller api modules...)

Yes, I believe that's correct. There's a lot more boilerplate involved if I recall correctly, but yes 😄

I know you showed me an example of a kubernetes operator in Ruby, I think it was this one:
https://gitlab.com/tobiaskuntzsch/kubernetes-operator

Yep, that's the one.

It also uses kubeclient and I think I would prefer to use k8s-ruby but if you have an opinion about this I could probably be persuaded as I am not very deep into this project yet!

I don't have an opinion as I have not actually used either one 😅 It might be of interest to you that Krane, the gem Kuby uses to deploy into k8s clusters, uses kubeclient.

@kingdonb
Copy link
Author

It would be really cool if there was an easy way to compose an open API schema using KubeDSL, potentially leveraging method_missing or some such

I don't know if there's a more standard way to compose openAPI schemas so we can maybe import it directly there by reference, like Swagger, but I was just going to search for one and landed instead at: https://openapi.tools/ – I assume there is a canonical representation (maybe in JSON), and there's a way to include it without actually nesting KeyValueFields.

That is helpful to know about Krane, I was trying to figure out how Kuby applies the resources without going too deeply 👍

I used k8s-ruby before and it was a snap to configure. I remember that I figured out pretty quickly, it was also easy to use to access even custom resources, there was little configuration required, pretty much "know the name of the API group" is it.

it's so cool you're creating a CRD and an accompanying Ruby operator - I'm really curious to see how that process goes. The k8s world tends to be so dominated by Go, it's nice to see forays by other languages. Nothing against Go of course, but Ruby is cool too :)

So I have this little stats project, such that I'm working on CSV data again, and I'd rather just do it in the language where I'm already expert on all the pitfalls and which tools let you jump over those problems most rapidly. I could do the CSV part in Ruby and shell out from Go, but then I guess, what's the point of using Go exactly? (Oh yeah, to learn the controller runtime...)

I have to learn to use the controller-runtime to build controllers in Go. That is obviously where most controllers are built and that's where the bulk of the nice tooling is, for Kubernetes controller making. It's an advanced Go package with Kubernetes behind it, I'm just not super familiar with Go even having worked with it for a while; I've been working with Ruby for about 10x longer functionally, so there are more tools I know that I can reach for without looking it up and doing some kind of trade-off risk analysis of which tools are better, or without polling the experts on questions that they might not be interested in.

In particular, I spent at least 5 years learning to work with CSV data in Ruby. So I know that I can just write a dozen or two dozen lines of Ruby and be done with that part of the work. I want to present the results as a web view. Again, easy in Ruby.

And then there's the fact that, this particular CSV manipulation really doesn't have any business being on Kubernetes at all. It would be different if I had a project that I needed to do, which had strong libraries in Go for the things I need to "finish" it, if they were libraries that I was already familiar with.

And I could just do it the old fashioned way, with a database and ActiveRecord, then I'm just working on CSV data in Ruby, ...and that's what I was doing over 5 years ago. I work on Kubernetes for my job, I am selling Kubernetes when I give talks; if I'm writing code and it's not on Kubernetes, that would be bad for my professional development and future career prospects.

I agree that Ruby is cool, and the reports of its demise are greatly exaggerated, (just like they were 5 years ago.) I think more Kubernetes projects should use it, and I think more Ruby projects will run on Kubernetes, so writing Ruby apps that can be K8s-aware is and will continue to be an area worth exploring!

The last app I wrote that I mentioned with k8s-ruby simply ran on Kubernetes, the goal of connecting it to Kubernetes was only to demonstrate how we could implement visibility into Progressive Delivery with few hooks into the K8s API, and very little external tooling. Detect (1) if we are running on Kubernetes, (2) if Flagger is available/if the CRD for Canary resource was present on the cluster/if we have permission to read them in our namespace, and finally (3) check out the Canary status if it exists...

So we could report through the app's Admin panel about number of replicas in the Deployment(s), find out the name and number of the pods and their statuses as the rollouts (eg. progressive delivery) are ongoing, ... the status Canary has (it can transition through any of initialized/progressing/succeeded/failed etc.) – I'll probably come back and do those things again in this app when the business parts are all finished. Then, maybe something using the Kubernetes Downward API comes next! IDK, I think it will play well for people to see that not only can Kubernetes stuff be done in Ruby, but that it's also easy.

Anyway, as I said this is "baby's first CRD operator" it's nearly guaranteed that I won't implement Kstatus correctly, or Conditions at all, or the patterns of controller-runtime in even remotely close to accepted practices, ...and that's all fine. I'm not doing this to set a perfect example, I'm doing it as 90% learning experience and 10% get something done that I needed!

And as I've got all this experience in CSV manipulation from the before-times, doing it this way will make it possible for me to spend the 90% time on the 90% thing (learning how to do Kubernetes things in Ruby again), and the 10% time on the 10% thing (doing CSV stuff)... instead of bucking up against "the other 90%" you always hear about, you know, the 90/10 rule 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants